﻿/* =====================================================================================================
 * C# Pawn Compiler
 * Copyright (c) agehack a.k.a. GUnitSoldier 2012
 * Source code released under Sun Public License (http://www.opensource.org/licenses/SPL)
===================================================================================================== */

// TODO: Seems that compiler can accept several -i options, so we can specify several includes directory

using System;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace IvmpEditor
{
    class PawnCompiler : ICompiler
    {
        public string InfoText { get { return "Pawn compiler library (c) 1998-2007 ITB CompuPhase"; } }
        public PawnCompiler(string app_dir) { appDir = new DirectoryInfo(app_dir); }
        public bool IsCompileDone { get { return compiledOk; } }
        public bool IsCompileAborted { get { return !IsCompileDone && abort; } }
        bool abort = false;
        DirectoryInfo appDir = null;
        bool compiledOk = false;
        string scriptName = null;
        DirectoryInfo scriptDir = null;
        LockFile lockFile = new LockFile("pawn_compiler.lock");
        public string IncludesDirectoryPath = @"..\pawno\include";

        // Events:
        public event _Compiler.SendString SetStatus;
        public event _Compiler.SendString FatalError;
        public event _Compiler.Signal CompilationStarted;
        public event _Compiler.Signal CompilationFinished;

        // General methods:
        public void Compile(string sourceFileName, Encoding src_encd)
        {
            // Compiler works only with .pwn files.
            if (!sourceFileName.ToLower().EndsWith(".pwn"))
                throw new NotSupportedException("This is not PAWN script source file (.pwn)?");

            compiledOk = abort = false;
            CompilationStarted(this);
            try
            {
                // Lock our lock file to prevent running several compiler instances
                _Compiler.HoldCompilerLock(lockFile, ref abort);

                // Get scriptname & etc info.
                scriptDir = new FileInfo(sourceFileName).Directory;
                int i = sourceFileName.LastIndexOf("\\") + 1;
                scriptName = sourceFileName.Substring(i, sourceFileName.Length - i);
                scriptName = scriptName.Substring(0, scriptName.ToLower().LastIndexOf(".pwn"));
                var sourceFile = new FileInfo(sourceFileName);
                var errorsLogFile = new FileInfo(appDir + @"\err.log");

                // Includes directory path
                var includesDir = new DirectoryInfo(Common.ToAbsolutePath(IncludesDirectoryPath, sourceFile.Directory));
                SetStatus(this, "Pawn includes directory: " + includesDir);

                // Run compiler process:
                var pawncc = new Process();
                pawncc.StartInfo.FileName = appDir + @"\pawncc.exe";
                pawncc.StartInfo.Arguments = string.Format("\"{0}\" -e\"{1}\" -i\"{2}\"", sourceFileName, errorsLogFile, includesDir);
                pawncc.StartInfo.WorkingDirectory = scriptDir.FullName;
                pawncc.StartInfo.CreateNoWindow = true;
                pawncc.StartInfo.UseShellExecute = false;
                pawncc.Start();
                if (!_Compiler.WaitForProcessExit(ref abort, pawncc))
                    throw new ThreadStateException();
                compiledOk = (pawncc.ExitCode == 0);

                // Read info from log file:
                if (errorsLogFile.Exists)
                {
                    // Read info from log file:
                    string logText = string.Format("Compiler output:{0}{1}", Environment.NewLine, File.ReadAllText(errorsLogFile.FullName, src_encd));
                    errorsLogFile.Delete();
                    logText = logText.Replace(sourceFile.FullName, scriptName + ".pwn").Trim();
                    SetStatus(this, logText.Trim());
                }

                // Remove compiler log file:
                if (errorsLogFile.Exists)
                    errorsLogFile.Delete();
            }
            catch (Exception ex)
            {
                // Fatal error (something went wrong ...)
                compiledOk = false;
                if (!abort)
                    this.FatalError(this, ex.ToString());
            }

            // Unlock, cleanup, finish
            lockFile.Release();
            CompilationFinished(this);
        }
        public void RequestAbort() { abort = true; }
    }
}
